package org.teamh.gui.engine.model;

import static org.lwjgl.opengl.GL11.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.ByteBuffer;

import org.lwjgl.util.glu.GLU;

/**
 * This class is a singleton instance of a texture loader. It handles the
 * requests forward to the specific loader.
 * 
 * @author richard
 * 
 */
public class TextureLoader {
	private static final String BMP = "bmp";
	private static final String PNG = "png";
	private static final String JPG = "jpg";
	private static final String JPEG = "jpeg";
	private static final String TIFF = "tiff";
	private static final String TIF = "tif";
	private static final String[] EXTENSIONS = {BMP, PNG, JPG, JPEG, TIFF, TIF};
	
	private TextureLoader() {}
	
	/**
	 * Loads an image. Min and Mag filter are set to GL_LINEAR and the t and s wrap is set to GL_REPEAT. The image is not mipmapped. 
	 * @param file
	 * @return
	 */
	public static int loadTexture(String file) {
		return loadTexture(file, GL_LINEAR, GL_LINEAR, false);
	}
	
	/**
	 * Loads an image. The t and s wrap is set to GL_REPEAT. 
	 * @param file
	 * @param minFilter
	 * @param magFilter
	 * @param mipmapped
	 * @return
	 */
	public static int loadTexture(String file, int minFilter, int magFilter, boolean mipmapped) {
		return loadTexture(file, minFilter, magFilter, GL_REPEAT, GL_REPEAT, mipmapped);
	}
	
	/**
	 * Loads an image. 
	 * @param file
	 * @param minFilter
	 * @param magFilter
	 * @param textureWrapS
	 * @param textureWrapT
	 * @param mipmapped
	 * @return
	 */
	public static int loadTexture(String file, int minFilter, int magFilter, int textureWrapS, int textureWrapT, boolean mipmapped) {
		URL url = TextureLoader.class.getClassLoader().getResource(file);
		File f = new File(file);
		
		if(url == null && !f.exists()) {
			System.err.println("Cannot find: " + file);
			return 0;
		}
		
		InputStream input = null;
		int fileSize = 0;
		
		if(url != null) {
			fileSize = (int)new File(url.getPath()).length();
			try {
				input = new FileInputStream(url.getFile());
			} catch (IOException e) {
				System.err.println("Error occured while loading file as a url");
				return 0;
			}
		} else {
			fileSize = (int)f.length();
			try {
				input = new FileInputStream(f);
			} catch (FileNotFoundException e) {
				System.err.println("Error occured while loading file as a file");
				return 0;
			}
		}
		
		String extension = getExtension(file);
		if(extension == null) {
			System.err.println("This is not a valid image file");
			try {
				input.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return 0;
		}
		
		RawData rawData = null;
		
		if(extension.equals(BMP)) {
			
		} else if(extension.equals(PNG)) {
			rawData = PNGLoader.getPNG(input, fileSize);
		} else if(extension.equals(JPG) || extension.equals(JPEG)) {
			
		} else if(extension.equals(TIFF) || extension.equals(TIF)) {
			
		}
		
		if(rawData == null) {
			System.err.println("The image is not valid!");
			return 0;
		}
		
		int texture = glGenTextures();
		glBindTexture(GL_TEXTURE_2D, texture);
		
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, textureWrapS);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, textureWrapT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
		
		rawData.textureBuffer.rewind();
		
		if (mipmapped) {
			GLU.gluBuild2DMipmaps(GL_TEXTURE_2D, rawData.dstPixelFormat,
					rawData.width, rawData.height, rawData.srcPixelFormat,
					GL_UNSIGNED_BYTE, rawData.textureBuffer);
		} else {
			glTexImage2D(GL_TEXTURE_2D, 0, rawData.dstPixelFormat, rawData.width, 
					rawData.height, 0,
					rawData.srcPixelFormat, GL_UNSIGNED_BYTE, rawData.textureBuffer);
		}
		
		glBindTexture(GL_TEXTURE_2D, 0);
		
		try {
			input.close();
		} catch (IOException e) {
			System.out.println("Error occured while closing the input stream");
		}
		
		return texture;
	}
	
	/**
	 * Returns the extension of the file. 
	 * @param file
	 * @return
	 */
	private static String getExtension(String file) {
		String ext = null;
        int i = file.lastIndexOf('.');

        if (i > 0 &&  i < file.length() - 1) {
            ext = file.substring(i+1).toLowerCase();
        }
        
        for(i = 0; i < EXTENSIONS.length; i++) {
        	if(ext.equals(EXTENSIONS[i])) {
        		return ext;
        	}
        }
        
        return null;
	}
	
	/**
	 * This class repesents the raw data of an image file. After extracting, any image should be of the same format. 
	 * @author richard
	 *
	 */
	public static class RawData {
		private int width;
		private int height;
		private int dstPixelFormat;
		private int srcPixelFormat;
		private ByteBuffer textureBuffer;
		
		public RawData(int width, int height, int dstPixelFormat, int srcPixelFormat, ByteBuffer textureBuffer) {
			this.width = width;
			this.height = height;
			this.dstPixelFormat = dstPixelFormat;
			this.srcPixelFormat = srcPixelFormat;
			this.textureBuffer = textureBuffer;
		}
		
		public ByteBuffer getTextureBuffer() {
			return textureBuffer;
		}
		
		public int getWidth() {
			return width;
		}
		
		public int getHeight() {
			return height;
		}
		
		public int getDstPixelFormat() {
			return dstPixelFormat;
		}
		
		public int getSrcPixelFormat() {
			return srcPixelFormat;
		}
	}
}
